package com.wildwolf.project.wildwolf.service;

import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;

import net.java.otr4j.OtrException;
import net.java.otr4j.session.SessionID;

import org.jivesoftware.smack.Chat;
import org.jivesoftware.smack.XMPPException;
import org.jivesoftware.smack.util.StringUtils;
import org.jivesoftware.smackx.ChatState;
import org.jivesoftware.smackx.ChatStateListener;

import android.os.Environment;
import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.util.Log;

import com.wildwolf.project.wildwolf.otr.WildwolfOtrManager;
import com.wildwolf.project.wildwolf.service.aidl.IChat;
import com.wildwolf.project.wildwolf.service.aidl.IMessageListener;

/**
 * smack聊天的适配器
 * 
 * @author darisk
 */
public class ChatAdapter extends IChat.Stub {
	/**
	 * 聊天历史记录最大值
	 */
	private static final int HISTORY_MAX_SIZE = 1000;
	private static final String TAG = "ChatAdapter";
	private static final String PROTOCOL = "XMPP";

	private final Chat mAdaptee;
	private final Contact mParticipant;
	private String mState;
	/**
	 * 是否被打开
	 */
	private boolean mIsOpen;
	/**
	 * 消息列表
	 */
	private final List<Message> mMessages;
	private final RemoteCallbackList<IMessageListener> mRemoteListeners = new RemoteCallbackList<IMessageListener>();
	private final MsgListener mMsgListener = new MsgListener();
	private SessionID mOtrSessionId;
	private boolean mIsHistory;
	private File mHistoryPath;
	private String mAccountUser;

	/**
	 * 
	 * @param chat
	 */
	public ChatAdapter(final Chat chat) {
		mAdaptee = chat;
		mParticipant = new Contact(chat.getParticipant());
		mMessages = new LinkedList<Message>();
		mAdaptee.addMessageListener(mMsgListener);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public Contact getParticipant() throws RemoteException {
		return mParticipant;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void sendMessage(
			com.wildwolf.project.wildwolf.service.Message message)
			throws RemoteException {
		com.wildwolf.project.wildwolf.service.Message encrypted = otrEncryptMessage(message);
		if (encrypted != null) {
			transferMessage(encrypted);
		} else {
			transferMessage(message);
		}
		addMessage(message);
	}

	/**
	 * 发送聊天消息 暂时没有处理发送失败情况
	 * 
	 * @param message
	 */
	private void transferMessage(
			com.wildwolf.project.wildwolf.service.Message message) {
		org.jivesoftware.smack.packet.Message send = new org.jivesoftware.smack.packet.Message();
		String msgBody = message.getBody();
		send.setTo(message.getTo());
		Log.w(TAG, "message to " + message.getTo());
		send.setBody(msgBody);
		send.setThread(message.getThread());
		send.setSubject(message.getSubject());
		send.setType(org.jivesoftware.smack.packet.Message.Type.chat);
		try {
			mAdaptee.sendMessage(send);
		} catch (XMPPException e) {
			e.printStackTrace();
		}
	}

	/**
	 * send message.
	 * 
	 * @param msg
	 *            to send.
	 */
	public void injectMessage(String msg) {
		Message msgToSend = new Message(mParticipant.getJIDWithRes(),
				Message.MSG_TYPE_CHAT);
		msgToSend.setBody(msg);
		transferMessage(msgToSend);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void addMessageListener(IMessageListener listen) {
		if (listen != null)
			mRemoteListeners.register(listen);
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void removeMessageListener(IMessageListener listen) {
		if (listen != null) {
			mRemoteListeners.unregister(listen);
		}
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public String getState() throws RemoteException {
		return mState;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setState(String state) throws RemoteException {
		mState = state;
	}

	/**
	 * Get the adaptee for the Chat.
	 * 
	 * @return The real chat object
	 */
	public Chat getAdaptee() {
		return mAdaptee;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public void setOpen(boolean isOpen) {
		this.mIsOpen = isOpen;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public boolean isOpen() {
		return mIsOpen;
	}

	/**
	 * {@inheritDoc}
	 */
	@Override
	public List<Message> getMessages() throws RemoteException {
		return Collections.unmodifiableList(mMessages);
	}

	/**
	 * Add a message in the chat history.
	 * 
	 * @param msg
	 *            the message to add
	 */
	private void addMessage(Message msg) {
		if (mMessages.size() == HISTORY_MAX_SIZE)
			mMessages.remove(0);
		mMessages.add(msg);
		if (!"".equals(msg.getBody()) && msg.getBody() != null) {
			logMessage(msg);
		}
	}

	/**
	 * Save message in SDCard.
	 * 
	 * @param msg
	 *            the message receive
	 * @param contactName
	 *            the name of the contact
	 */
	public void saveHistory(Message msg, String contactName) {
		File path = getHistoryPath();
		File filepath;
		if (contactName.equals(msg.getFrom()))
			filepath = new File(path, StringUtils.parseBareAddress(contactName));
		else
			filepath = new File(path, StringUtils.parseBareAddress(msg.getTo()));
		path.mkdirs();
		try {
			FileWriter file = new FileWriter(filepath, true);
			String log = msg.getTimestamp() + " " + contactName + " "
					+ msg.getBody() + System.getProperty("line.separator");
			file.write(log);
			file.close();
		} catch (IOException e) {
			Log.e(TAG, "Error writing chat history", e);
		}
	}

	/**
	 * set History enable/disable.
	 * 
	 * @param isHisory
	 *            history state
	 */
	public void setHistory(boolean isHisory) {
		this.mIsHistory = isHisory;
	}

	/**
	 * get History state.
	 * 
	 * @return mIsHistory
	 */
	public boolean getHistory() {
		return mIsHistory;
	}

	/**
	 * Set Account user name.
	 * 
	 * @param accountUser
	 *            user name
	 */
	public void setAccountUser(String accountUser) {
		mAccountUser = accountUser;
	}

	/**
	 * get Account user name.
	 * 
	 * @return mAccountUser
	 */
	public String getAccountUser() {
		return mAccountUser;
	}

	/**
	 * set History path.
	 * 
	 * @param historyPath
	 *            history path
	 */
	public void setHistoryPath(File historyPath) {
		this.mHistoryPath = historyPath;
	}

	/**
	 * get History path.
	 * 
	 * @return mHistoryPath;
	 */
	public File getHistoryPath() {
		return mHistoryPath;
	}

	/**
	 * log a message.
	 * 
	 * @param message
	 *            message to log
	 */
	private void logMessage(
			com.wildwolf.project.wildwolf.service.Message message) {
		String state = Environment.getExternalStorageState();
		if (mIsHistory && Environment.MEDIA_MOUNTED.equals(state))
			saveHistory(message, mAccountUser);

	}

	/**
	 * encrypt a message with an otr session.
	 * 
	 * @param unencrypted
	 *            message with cleartext body
	 * @return message with encrypted body
	 */
	private com.wildwolf.project.wildwolf.service.Message otrEncryptMessage(
			com.wildwolf.project.wildwolf.service.Message unencrypted) {

		if (mOtrSessionId != null && unencrypted != null
				&& unencrypted.getBody() != null) {
			try {
				String body = WildwolfOtrManager.getInstance().getOtrManager()
						.transformSending(mOtrSessionId, unencrypted.getBody());
				Message result = new Message(unencrypted.getTo(),
						unencrypted.getType());
				result.setBody(body);
				return result;
			} catch (OtrException e) {
				Log.e(TAG, "OTR: Unable to encrypt message", e);
			}
		}
		return null;
	}

	/**
	 * Listener.
	 */
	private class MsgListener implements ChatStateListener {
		/**
		 * Constructor.
		 */
		public MsgListener() {
		}

		@Override
		public void processMessage(Chat chat,
				org.jivesoftware.smack.packet.Message message) {
			Message msg = new Message(message);
			Log.d(TAG, "new msg " + msg.getBody());
			String body;

			if (mOtrSessionId != null) {
				try {
					body = WildwolfOtrManager.getInstance().getOtrManager()
							.transformReceiving(mOtrSessionId, msg.getBody());
					msg.setBody(body);
				} catch (OtrException e) {
					Log.w(TAG, "Unable to decrypt OTR message", e);
				}
			}
			// TODO add que les message pas de type errors
			ChatAdapter.this.addMessage(msg);
			final int n = mRemoteListeners.beginBroadcast();
			for (int i = 0; i < n; i++) {
				IMessageListener listener = mRemoteListeners
						.getBroadcastItem(i);
				try {
					if (listener != null)
						listener.processMessage(ChatAdapter.this, msg);
				} catch (RemoteException e) {
					Log.w(TAG, "Error while diffusing message to listener", e);
				}
			}
			mRemoteListeners.finishBroadcast();
		}

		/**
		 * {@inheritDoc}
		 */
		@Override
		public void stateChanged(Chat chat, ChatState state) {
			mState = state.name();
			final int n = mRemoteListeners.beginBroadcast();

			for (int i = 0; i < n; i++) {
				IMessageListener listener = mRemoteListeners
						.getBroadcastItem(i);
				try {
					listener.stateChanged(ChatAdapter.this);
				} catch (RemoteException e) {
					Log.w(TAG, e.getMessage());
				}
			}
			mRemoteListeners.finishBroadcast();
		}

	}

	/**
	 * 当Otr会话状态被改变的时候执行
	 * 
	 * @param otrState
	 */
	public void otrStateChanged(final String otrState) {
		Message message = new Message(null, Message.MSG_TYPE_INFO);
		message.setBody(otrState);
		addMessage(message);
		final int n = mRemoteListeners.beginBroadcast();

		for (int i = 0; i < n; i++) {
			IMessageListener listener = mRemoteListeners.getBroadcastItem(i);
			try {
				listener.otrStateChanged(otrState);
			} catch (RemoteException e) {
				Log.w(TAG, e.getMessage());
			}
		}
		mRemoteListeners.finishBroadcast();
	}

	@Override
	public void startOtrSession() throws RemoteException {
		if (mOtrSessionId == null) {
			mOtrSessionId = new SessionID(mAccountUser,
					mParticipant.getJIDWithRes(), PROTOCOL);
			WildwolfOtrManager.getInstance().addChat(mOtrSessionId, this);
		}

		try {
			WildwolfOtrManager.getInstance().getOtrManager()
					.startSession(mOtrSessionId);
		} catch (OtrException e) {
			mOtrSessionId = null;
			e.printStackTrace();
			throw new RemoteException();
		}
	}

	@Override
	public void endOtrSession() throws RemoteException {
		try {
			localEndOtrSession();
		} catch (OtrException e) {
			e.printStackTrace();
			throw new RemoteException();
		}
	}

	/**
	 * end an Otr session.
	 * 
	 * @return false if something bad happened.
	 * @throws OtrException
	 *             an exception from otr
	 */
	public boolean localEndOtrSession() throws OtrException {
		if (mOtrSessionId == null)
			return true;

		WildwolfOtrManager.getInstance().getOtrManager()
				.endSession(mOtrSessionId);
		WildwolfOtrManager.getInstance().removeChat(mOtrSessionId);
		mOtrSessionId = null;
		listenOtrSession();
		return true;
	}

	public void listenOtrSession() {
		if (mOtrSessionId != null)
			return;

		mOtrSessionId = new SessionID(mAccountUser,
				mParticipant.getJIDWithRes(), PROTOCOL);
		WildwolfOtrManager.getInstance().addChat(mOtrSessionId, this);
		// OtrEngineImpl will make a call to "this.getSession(sessionID)" which
		// will instantiate our session.
		WildwolfOtrManager.getInstance().getOtrManager()
				.getSessionStatus(mOtrSessionId);
	}

	@Override
	public String getLocalOtrFingerprint() throws RemoteException {
		if (mOtrSessionId == null)
			return null;

		return WildwolfOtrManager.getInstance().getLocalFingerprint(
				mOtrSessionId);
	}

	@Override
	public String getRemoteOtrFingerprint() throws RemoteException {
		if (mOtrSessionId == null)
			return null;

		return WildwolfOtrManager.getInstance().getRemoteFingerprint(
				mOtrSessionId);
	}

	@Override
	public void verifyRemoteFingerprint(boolean ok) {
		if (mOtrSessionId != null) {
			if (ok)
				WildwolfOtrManager.getInstance().verifyRemoteFingerprint(
						mOtrSessionId);
			else
				WildwolfOtrManager.getInstance().unverifyRemoteFingerprint(
						mOtrSessionId);
		}
	}

	@Override
	public String getOtrStatus() throws RemoteException {
		if (mOtrSessionId == null)
			return null;
		return WildwolfOtrManager.getInstance().getOtrManager()
				.getSessionStatus(mOtrSessionId).toString();
	}
}
